using System;
using System.Collections.Generic;
using System.Text;
using System.Management;

namespace NAst.Common
{
    /// <summary>
    /// Change the default Printer
    /// </summary>
    [Netlet("DefaultPrinter")]
    public class ChangeDefaultPrinterNetlet : INetlet
    {
        string _defaultToSet = null;
        string _defaultToRestore = null;
        #region INetlet Members

        /// <summary>
        /// Read the configuration element 
        /// </summary>
        /// <param name="stateName">Name of the NetworkConfiguration</param>
        /// <param name="config">Configuration element</param>
        public void Initialize(string stateName, System.Xml.XmlElement config)
        {
            if (config == null)
                throw new ArgumentNullException("config");
            _defaultToSet = config.GetAttribute("printerName");
        }

        /// <summary>
        /// Change the default printer
        /// </summary>
        public void Connect()
        {
            if (string.IsNullOrEmpty(_defaultToSet))
                return;

            using (ManagementObjectSearcher sr = new ManagementObjectSearcher("select * from Win32_Printer where default=1 or name=\"" + _defaultToSet + "\""))
            {
                ManagementObject oldPrinter;
                ManagementObject newPrinter;
                string currentDefaultName;
                FindPrinters(sr, _defaultToSet, out oldPrinter, out newPrinter, out currentDefaultName);

                if (oldPrinter != newPrinter)
                {
                    try
                    {
                        newPrinter.InvokeMethod("SetDefaultPrinter", new object[0]);
                        _defaultToRestore = currentDefaultName;
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Unable to set the default printer to " + _defaultToSet);
                    }
                }
            }
        }

        /// <summary>
        /// Revert to the previous default printer
        /// </summary>
        public void Disconnect()
        {
            if (string.IsNullOrEmpty(_defaultToRestore))
                return;

            using (ManagementObjectSearcher sr = new ManagementObjectSearcher("select * from Win32_Printer where default=1 or name=\"" + _defaultToRestore + "\""))
            {
                try
                {
                    ManagementObject oldPrinter;
                    ManagementObject newPrinter;
                    string currentDefaultName;
                    FindPrinters(sr, _defaultToRestore, out oldPrinter, out newPrinter, out currentDefaultName);

                    if (oldPrinter == newPrinter)
                        return;

                    if (!_defaultToSet.Equals(currentDefaultName, StringComparison.InvariantCultureIgnoreCase))
                        return;

                    newPrinter.InvokeMethod("SetDefaultPrinter", new object[0]);
                }
                catch(Exception e)
                {
                    throw new ApplicationException("Unable to set the default printer back to " + _defaultToRestore);
                }
            }
        }

        /// <summary>
        /// Save the current state of the netlet
        /// </summary>
        /// <param name="elmToSaveTo">Where to save</param>
        public void SaveState(System.Xml.XmlElement elmToSaveTo)
        {
            elmToSaveTo.SetAttribute("oldDefault", _defaultToRestore);
            elmToSaveTo.SetAttribute("newDefault", _defaultToSet);
        }

        /// <summary>
        /// Load state from a previously saved one
        /// </summary>
        /// <param name="elmToLoadFrom">From where to load</param>
        public void LoadState(System.Xml.XmlElement elmToLoadFrom)
        {
            _defaultToRestore = elmToLoadFrom.GetAttribute("oldDefault");
            _defaultToSet = elmToLoadFrom.GetAttribute("newDefault");
        }

        #endregion


        /// <summary>
        /// Return the default printer and/or
        /// the one matching a name in a given
        /// WMI search
        /// </summary>
        /// <param name="sr"></param>
        /// <param name="nameToFind"></param>
        /// <param name="oldPrinter"></param>
        /// <param name="newPrinter"></param>
        /// <param name="currentDefaultName"></param>
        private void FindPrinters(ManagementObjectSearcher sr, 
            string nameToFind, 
            out ManagementObject oldPrinter, 
            out ManagementObject newPrinter, 
            out string currentDefaultName)
        {
            ManagementObjectCollection printers = sr.Get();
            oldPrinter = null;
            newPrinter = null;
            currentDefaultName = null;
            foreach (ManagementObject temp in printers)
            {
                string name = ((string)temp.GetPropertyValue("Name"));
                bool isdefault = ((bool)temp.GetPropertyValue("Default"));

                if (name.Equals(nameToFind, StringComparison.InvariantCultureIgnoreCase))
                    newPrinter = temp;
                if (isdefault)
                {
                    oldPrinter = temp;
                    currentDefaultName = name;
                }
            }

            if (newPrinter == null)
                throw new ApplicationException("Can't find a printer named " + _defaultToSet);

        }

    }
}
